#include "ResourcesManager.h"


ResourcesManager::ResourcesManager(void)
	:m_pDevice(NULL)
	,m_iMeshIDBase(0)
{
}


ResourcesManager::~ResourcesManager(void)
{
	Clear();
}

ResourcesManager* ResourcesManager::Instance()
{
	static ResourcesManager* m_instance = NULL;
	if(!m_instance)
		m_instance = new ResourcesManager();
	return m_instance;
}

void ResourcesManager::Init( ID3D10Device* device )
{
	Instance()->SetDevice(device);
}

void ResourcesManager::SetDevice( ID3D10Device* device )
{
	m_pDevice = device;
}

ID3D10ShaderResourceView* ResourcesManager::CreateTexture( const std::string& texName )
{
	HRESULT hr;
	ID3D10ShaderResourceView* ret;
	std::wstring name = ToWstring(texName);
	hr = D3DX10CreateShaderResourceViewFromFile(m_pDevice,name.c_str()
		,0,0,&ret,0);
	if(FAILED(hr))
	{
		//::MessageBoxA(NULL,"Get Texture Error",0,0);
		name = ToWstring("./Resource/" + texName);
		hr = D3DX10CreateShaderResourceViewFromFile(m_pDevice,name.c_str()
			,0,0,&ret,0);
		if(FAILED(hr))
		{
			::MessageBoxA(NULL,"Get Texture Error",0,0);
			return NULL;
		}
	}


	return ret;
}

ID3D10ShaderResourceView* ResourcesManager::GetTexture( const std::string& texName )
{

	std::map<std::string,ID3D10ShaderResourceView*>::const_iterator iter;
	iter = m_TextureMap.find(texName);
	if(iter != m_TextureMap.end())
		return iter->second;

	ID3D10ShaderResourceView* ret;
	ret = CreateTexture(texName);

	m_TextureMap.insert(make_pair(texName,ret));

	return ret;
}

void ResourcesManager::Clear()
{
	std::map<std::string,ID3D10ShaderResourceView*>::iterator iterTexture;
	for(iterTexture = m_TextureMap.begin() ;iterTexture != m_TextureMap.end() ;++iterTexture)
		ReleaseCOM(iterTexture->second);

	//clear material set
	std::vector<MaterialSet*>::iterator iterMaterialSet;
	for(iterMaterialSet=m_lstMaterialSet.begin() ;iterMaterialSet!=m_lstMaterialSet.end() ;++iterMaterialSet)
		delete (*iterMaterialSet);

	//clear mesh
	std::vector<Mesh*>::iterator iterMesh;
	for(iterMesh=m_lstMesh.begin() ;iterMesh!=m_lstMesh.end() ;++iterMesh)
		delete (*iterMesh);
}

SceneNode* ResourcesManager::LoadModelFile( const std::string& name )
{
	//read the scene file by assimp
	Assimp::Importer importer;

	const aiScene* scene = importer.ReadFile(name, 
		//		aiProcess_CalcTangentSpace			| 
		aiProcess_Triangulate				|
		aiProcess_JoinIdenticalVertices		|
		aiProcess_FlipUVs					|
		aiProcess_MakeLeftHanded			|
		aiProcess_FlipWindingOrder			|
		aiProcess_SortByPType);

	// If the import failed, report it
	if(!scene)
	{
		//DoTheErrorLogging( importer.GetErrorString());
		Logger::Instance()->LogString(importer.GetErrorString());
		return NULL;
	}

	//LogSceneInfo(scene);

	//read the material set from current scene
	int currMaterialSetID = m_lstMaterialSet.size();

	m_lstMaterialSet.push_back(ReadMaterialSetFromScene(scene));

	Logger::Instance()->LogString("load material success");

	//copy meshes and set the mesh id base,every scene has a mesh base ID
	m_iMeshIDBase = m_lstMesh.size();

	for(unsigned int i=0 ;i<scene->mNumMeshes ;++i)
	{
		m_lstMesh.push_back(MakeMesh(scene->mMeshes[i],currMaterialSetID,i));
	}

	Logger::Instance()->LogString("Load mesh success");

	//load node hierarchy
	SceneNode* ret = new SceneNode();

	LoadNodeWithMeshes(scene->mRootNode , ret);

	SceneNode* pNode = ret->FindNodeByName("Camera");
	if(pNode)
		Logger::Instance()->Log("camera is attach a node");

	return ret;
}

MaterialSet* ResourcesManager::ReadMaterialSetFromScene( const aiScene* scene )
{
	MaterialSet *currMaterialSet = new MaterialSet();

	Material material;
	D3DXCOLOR color;
	aiColor3D aiColor;
	float shininess;

	for(unsigned int i=0 ;i<scene->mNumMaterials ;++i)
	{
		//get the diffuse
		scene->mMaterials[i]->Get(AI_MATKEY_COLOR_DIFFUSE,aiColor);
		material.SetDiffuse(Convert_aiColor_To_D3DXCOLOR(aiColor));

		//get the ambient
		scene->mMaterials[i]->Get(AI_MATKEY_COLOR_AMBIENT,aiColor);
		material.SetAmbient(Convert_aiColor_To_D3DXCOLOR(aiColor));

		//get the specular
		scene->mMaterials[i]->Get(AI_MATKEY_COLOR_SPECULAR,aiColor);
		material.SetSpecular(Convert_aiColor_To_D3DXCOLOR(aiColor));

		scene->mMaterials[i]->Get(AI_MATKEY_SHININESS,shininess);
		material.SetShininess(shininess);

		currMaterialSet->AddMaterial(material);
	}

	return currMaterialSet;
}

Mesh* ResourcesManager::MakeMesh( const aiMesh* mesh,int MaterialSetID ,int ID )
{
	Mesh* ret;

	ret = new Mesh();

	if(!ret)
	{
		Logger::Instance()->LogString("make mesh fail,new Mesh() error");
	}

	//copy the device
	ret->m_pDeivce = m_pDevice;

	//copy name and number info
	ret->m_strName = mesh->mName.data;

	//make mesh ID
	ret->m_iMeshID = m_iMeshIDBase + ID;

	ret->m_iNumVerteices = mesh->mNumVertices;
	ret->m_iNumBones = mesh->mNumBones;
	ret->m_iNumFaces = mesh->mNumFaces;

	//copy material(set) info
	ret->m_MaterialInfo.m_iMaterialSetID = MaterialSetID;
	ret->m_MaterialInfo.m_iMaterialIndex = mesh->mMaterialIndex;

	MeshVertex meshVertex;
	//D3DXVECTOR3 _normal(0.0f,1.0f,0.0f);
	//copy vertex position and normals
	for(unsigned int i=0 ;i<mesh->mNumVertices ;++i)
	{
		//ret->m_lstVertexPos.push_back(Convert_aiVector3D_To_D3DXVECTOR3(mesh->mVertices[i]));
		//ret->m_lstVertexNormal.push_back(Convert_aiVector3D_To_D3DXVECTOR3(mesh->mNormals[i]));
		meshVertex.pos = Convert_aiVector3D_To_D3DXVECTOR3(mesh->mVertices[i]);
		meshVertex.normal = Convert_aiVector3D_To_D3DXVECTOR3(mesh->mNormals[i]);
		//meshVertex.normal = Convert_aiVector3D_To_D3DXVECTOR3(mesh->mNormals[i]);
		//meshVertex.normal = _normal;
		ret->m_lstVertex.push_back(meshVertex);
	}

	//copy bones , but the chess has no bones ,so we do nothing

	//copy faces
	for(unsigned int i=0 ;i<mesh->mNumFaces ;++i)
	{
		ret->m_lstFaces.push_back(Convert_aiFace_To_Face(mesh->mFaces[i]));
		ret->m_lstIndex.push_back(ret->m_lstFaces[i].m_lstIndices[0]);
		ret->m_lstIndex.push_back(ret->m_lstFaces[i].m_lstIndices[1]);
		ret->m_lstIndex.push_back(ret->m_lstFaces[i].m_lstIndices[2]);
	}

	//build the AABB bounding box
	ret->BuildBoundingBox();

	return ret;
}

void ResourcesManager::LoadNodeWithMeshes( aiNode* node,SceneNode* sceneNode,SceneNode* parent/*=NULL*/ )
{
	//copy node information
	sceneNode->m_iNumMeshs = node->mNumMeshes;
	sceneNode->m_iNumChildren = node->mNumChildren;
	sceneNode->m_strSceneNodeName = node->mName.data;
	sceneNode->m_pParent = parent;

	//Logger::Instance()->Log("Load Node "+sceneNode->m_strSceneNodeName);

	//copy meshes ID,plus them with base ID
	for(unsigned int i=0 ;i<node->mNumMeshes ;++i)
	{
		sceneNode->m_lstMeshs.push_back(node->mMeshes[i] + m_iMeshIDBase);
	}

	//children
	for(unsigned int i=0 ;i<node->mNumChildren ;++i)
	{
		SceneNode* children = new SceneNode();
		sceneNode->m_lstChildren.push_back(children);
		LoadNodeWithMeshes(node->mChildren[i],children,sceneNode);
	}
}


//////////assimp convert stuff///////////////////////////////////////////////////////////

D3DXVECTOR3 ResourcesManager::Convert_aiVector3D_To_D3DXVECTOR3( const aiVector3D& vec )
{
	D3DXVECTOR3 ret;

	ret.x = vec.x;
	ret.y = vec.y;
	ret.z = vec.z;

	return ret;
}

D3DXVECTOR3 ResourcesManager::Convert_aiVector3D_To_D3DXVECTOR3( const aiVector3D* vec )
{
	D3DXVECTOR3 ret;

	ret.x = vec->x;
	ret.y = vec->y;
	ret.z = vec->z;

	return ret;
}

D3DXCOLOR ResourcesManager::Convert_aiColor_To_D3DXCOLOR( const aiColor3D& color )
{
	D3DXCOLOR	ret;
	ret.r = color.r;
	ret.g = color.g;
	ret.b = color.b;
	ret.a = 1.0f;
	return ret;
}

Face ResourcesManager::Convert_aiFace_To_Face( const aiFace& face )
{
	Face ret;

	ret.m_iNumIndices = face.mNumIndices;

	for(unsigned int i=0 ;i<face.mNumIndices ;++i)
	{
		ret.m_lstIndices.push_back(face.mIndices[i]);
	}

	return ret;
}

void ResourcesManager::Convert_aiMatrix_To_D3DXMATRIX( const aiMatrix4x4& aiMat,D3DXMATRIX& mat )
{
	//mat._11 = aiMat.a1;
	//mat._12 = aiMat.a2;
	//mat._13 = aiMat.a3;
	//mat._14 = aiMat.a4;

	//mat._21 = aiMat.b1;
	//mat._22 = aiMat.b2;
	//mat._33 = aiMat.b3;
	//mat._34 = aiMat.b4;

	//mat._31 = aiMat.c1;
	//mat._32 = aiMat.c2;
	//mat._33 = aiMat.c3;
	//mat._34 = aiMat.c4;

	//mat._41 = aiMat.d1;
	//mat._42 = aiMat.d2;
	//mat._43 = aiMat.d3;
	//mat._44 = aiMat.d4;
	mat._11 = aiMat.a1;
	mat._12 = aiMat.b1;
	mat._13 = aiMat.c1;
	mat._14 = aiMat.d1;

	mat._21 = aiMat.a2;
	mat._22 = aiMat.b2;
	mat._33 = aiMat.c2;
	mat._34 = aiMat.d2;

	mat._31 = aiMat.a3;
	mat._32 = aiMat.b3;
	mat._33 = aiMat.c3;
	mat._34 = aiMat.d3;

	mat._41 = aiMat.a4;
	mat._42 = aiMat.b4;
	mat._43 = aiMat.c4;
	mat._44 = aiMat.d4;
}

Mesh* ResourcesManager::GetMeshByID( int id )
{
	return m_lstMesh[id];
}

Material* ResourcesManager::GetMaterialByInfo( const MaterialInfo* info )
{
	return m_lstMaterialSet[info->m_iMaterialSetID]->GetMaterialByIndex(info->m_iMaterialIndex);
}

void ResourcesManager::BuildMeshBuffer()
{
	std::vector<Mesh*>::iterator iterMesh;

	for(iterMesh=m_lstMesh.begin() ;iterMesh!=m_lstMesh.end() ;++iterMesh)
	{
		(*iterMesh)->BuildVertexBuffer();
		(*iterMesh)->BuildIndexBuffer();
	}
}

void ResourcesManager::LogSceneInfo( const aiScene* pScene )
{
	Logger::Instance()->LogString("---------- scene info -------------");

	if(pScene->HasTextures())
		Logger::Instance()->LogString("this scene has texture");
	else
		Logger::Instance()->LogString("this scene has no texture");

	if(pScene->HasCameras())
		Logger::Instance()->LogString("this scene has cameras");

	if(pScene->HasLights())
		Logger::Instance()->LogString("this scene has lights");

	if(pScene->mNumMaterials)
	{
		const aiMaterial* pMaterial = pScene->mMaterials[0];
		Logger::Instance()->LogString("this scene has material");
		for(unsigned int i=0;i<pMaterial->mNumProperties;++i)
		{
			Logger::Instance()->LogString(pMaterial->mProperties[i]->mKey.data);
		}

	}
	else
		Logger::Instance()->LogString("this scene has no material");

	if(pScene->mNumMeshes)
	{
		const aiMesh* pMesh = pScene->mMeshes[0];
		if(sizeof(pMesh->mTextureCoords)/sizeof(aiVector3D) != 0)
			Logger::Instance()->LogString("mesh has UV");
	} 


	Logger::Instance()->LogString("---------------------------------");
}

//void ResourcesManager::LogSceneNodeInfo( SceneNode* pNode )
//{
//	Logger::Instance()->LogString("Node Name : "+pNode->m_strSceneNodeName);
//	std::list<int>::const_iterator iter;
//	for(iter=pNode->m_lstMeshs.begin() ;iter!=pNode->m_lstMeshs.end() ;++iter)
//	{
//		Mesh* pMesh = ResourcesManager::Instance()->GetMeshByID(*iter);
//		Material* pMaterial = ResourcesManager::Instance()->GetMaterialByInfo(pMesh->GetMaterialInfo());
//		Logger::Instance()->LogD3DXCOLOR(pMaterial->GetDiffuse());
//	}
//}
